第2章列举并解释了选择要执行的设备的五种方法。本质上，方法1是不规定内核在什么地方运行，而方法5则相反，会考虑在设备上执行一个精确的模型。介于两者之间的枚举方法提供了灵活性和规定性。图12-1、12-2和12-3说明了如何选择设备。\par

图12-1展示了实现会为选择一个默认设备(第2章中的方法1)，可以查询有关所选设备的信息。\par

图12-2展示了如何使用特定的设备(本例中是GPU)设置队列。如果没有可用的GPU，则在主机上显式地返回。这给了我们选择设备的控制权，如果简单地使用默认队列，最终可能会得到意想不到的设备类型(例如，DSP、FPGA)。如果明确地想要在没有GPU设备的情况下使用主机设备，代码则可以会做到。回想一下，主机设备总是存在的，所以使用host\_selector时并不用担心。\par

不建议使用如图12-2所示的解决方案。除了看起来有点吓人和容易出错之外，图12-2没有给我们选择什么GPU的控制权，如果有多个可用的GPU，其会依赖于实现进行选择。尽这个例子有教育意义和实用价值，但还是有更好的方法可以替代。建议编写自定义设备选择器，如下面的代码示例(图12-3)所示。\par

\hspace*{\fill} \par %插入空行
\textbf{自定义设备选择器}

图12-3使用自定义设备选择器。自定义设备选择器在第2章中作为方法5讨论，用来选择的代码运行的位置(图2-15)。自定义设备选择器会为应用程序可用的每个设备调用operator()，如图12-3所示。在这个例子中，选中的设备为得分最高的设备，我们将使用选择器进行一些有趣的操作:\par

\begin{itemize}
	\item 拒绝供应商名称包含“Martian”(返回-1)的GPU。
	\item 建议使用供应商名称包含单词“ACME”的GPU(返回824)。
	\item 任何其他GPU(返回799)。
	\item 如果没有GPU，我们选择主机设备(返回99)。
	\item 忽略所有设备(返回-1)。
\end{itemize}

下一节，“get\_info<>”深入研究了get\_devices()、get\_platforms()和get\_info<> offer的信息。这些接口为选择设备提供了逻辑参考，包括图2-15和12-3中所示的简单的供应商名称检查。\par

\hspace*{\fill} \par %插入空行
图12-1 使用默认分配的设备
\begin{lstlisting}[caption={}]
queue Q;

std::cout << "By default, we are running on "
	<< Q.get_device().get_info<info::device::name>() << "\n";

// sample output:
// By default, we are running on Intel(R) Gen9 HD Graphics NEO.
\end{lstlisting}

\begin{tcolorbox}[colback=red!5!white,colframe=red!75!black]
关于设备的查询依赖于已安装的软件(特殊的用户级驱动程序)。SYCL和DPC++也依赖这些软件，就像操作系统需要驱动程序来访问硬件一样——仅仅将硬件安装在一台机器上是不够的。
\end{tcolorbox}

\hspace*{\fill} \par %插入空行
图12-2 使用try-catch选择GPU设备和主机设备
\begin{lstlisting}[caption={}]
auto GPU_is_available = false;

try {
	device testForGPU((gpu_selector()));
	GPU_is_available = true;
} catch (exception const& ex) {
	std::cout << "Caught this SYCL exception: " << ex.what() << std::endl;
}

auto Q = GPU_is_available ? queue(gpu_selector()) : queue(host_selector());

std::cout << "After checking for a GPU, we are running on:\n "
	  	  << Q.get_device().get_info<info::device::name>() << "\n";

// sample output using a system with a GPU:
// After checking for a GPU, we are running on:
// Intel(R) Gen9 HD Graphics NEO.
// 
// sample output using a system with an FPGA accelerator, but no GPU:
// Caught this SYCL exception: No device of requested type available.
// ...(CL_DEVICE_NOT_FOUND)
// After checking for a GPU, we are running on:
// SYCL host device.
\end{lstlisting}

\hspace*{\fill} \par %插入空行
图12-3 自定义设备选择器——首选的解决方案
\begin{lstlisting}[caption={}]
class my_selector : public device_selector {
public:
	int operator()(const device &dev) const {
		int score = -1;
		
		// We prefer non-Martian GPUs, especially ACME GPUs
		if (dev.is_gpu()) {
			if (dev.get_info<info::device::vendor>().find("ACME")
				!= std::string::npos) score += 25;
			
			if (dev.get_info<info::device::vendor>().find("Martian")
				== std::string::npos) score += 800;
		}
	
		// Give host device points so it is used if no GPU is available.
		// Without these next two lines, systems with no GPU would select
		// nothing, since we initialize the score to a negative number above.
		if (dev.is_host()) score += 100;
		return score;
	}
};

int main() {
	auto Q = queue{ my_selector{} };
	
	std::cout << "After checking for a GPU, we are running on:\n "
			  << Q.get_device().get_info<info::device::name>() << "\n";
			  
	// Sample output using a system with a GPU:
	// After checking for a GPU, we are running on:
	// Intel(R) Gen9 HD Graphics NEO.
	// 
	// Sample output using a system with an FPGA accelerator, but no GPU:
	// After checking for a GPU, we are running on:
	// SYCL host device.
	return 0;
}
\end{lstlisting}

\hspace*{\fill} \par %插入空行
\textbf{get\_info<>}

为了让程序“知道”在运行时哪些设备可用，可以让程序从设备类中查询可用的设备，然后可以使用get\_info<>查询特定的设备来了解更多细节。我们提供了一个简单的程序，叫做curious(参见图12-4)，它使用这些接口输出信息直接查看。开发或调试使用这些接口的程序时，进行完整性检查非常有用，接口失败通常是软件驱动程序没有正确安装。图12-5显示了该程序的示例输出，其中包含了有关当前设备的高级信息。\par

\hspace*{\fill} \par %插入空行
图12-4 设备查询机制的简单使用:curious.cpp
\begin{lstlisting}[caption={}]
// Loop through available platforms
for (auto const& this_platform : platform::get_platforms() ) {
	std::cout << "Found platform: "
			  << this_platform.get_info<info::platform::name>() << "\n";
	
	// Loop through available devices in this platform
	for (auto const& this_device : this_platform.get_devices() ) {
		std::cout << " Device: "
				  << this_device.get_info<info::device::name>() << "\n";
	}
	std::cout << "\n";
}
\end{lstlisting}

\hspace*{\fill} \par %插入空行
图12-5 来自curious.cpp的示例输出
\begin{tcolorbox}[colback=white,colframe=black]
\% make curious \\
dpcpp curious.cpp -o curious\\
\\
\% ./curious \\
Found platform 1...\\
Platform: Intel(R) FPGA Emulation Platform for OpenCL(TM)\\
Device: Intel(R) FPGA Emulation Device\\
\\
Found platform 2...\\
Platform: Intel(R) OpenCL HD Graphics\\
Device: Intel(R) Gen9 HD Graphics NEO\\
\\
Found platform 3...\\
Platform: Intel(R) OpenCL\\
Device: Intel(R) Xeon(R) E-2176G CPU @ 3.70GHz\\
\\
Found platform 4...\\
Platform: SYCL host platform\\
Device: SYCL host device
\end{tcolorbox}

\hspace*{\fill} \par %插入空行
\textbf{了解更多: 详细的枚举}

程序命名为verycurious.cpp(图12-6)，以说明使用get\_info<>可以获得的详细信息。同样，发现自己编写这样的代码是为了在开发或调试程序时提供帮助。图12-5显示了该程序的示例输出，其中包含有关于当前设备的底层信息。\par

现在我们已经展示了如何访问信息，接下来将讨论在应用程序中最重要的查询和操作的信息字段。\par

\hspace*{\fill} \par %插入空行
图12-6 设备查询机制的更详细使用:verycurious.cpp
\begin{lstlisting}[caption={}]
template <auto query, typename T>
void do_query( const T& obj_to_query, const std::string& name, int indent=4) 
{
	std::cout << std::string(indent, ' ') << name << " is '"
		<< obj_to_query.template get_info<query>() << "'\n";
}

// Loop through the available platforms
for (auto const& this_platform : platform::get_platforms() ) {
	std::cout << "Found Platform:\n";
	do_query<info::platform::name>(this_platform,
		"info::platform::name");
	do_query<info::platform::vendor>(this_platform, 
		"info::platform::vendor");
	do_query<info::platform::version>(this_platform, 
		"info::platform::version");
	do_query<info::platform::profile>(this_platform, 
		"info::platform::profile");
		
	// Loop through the devices available in this plaform
	for (auto &dev : this_platform.get_devices() ) {
		std::cout << " Device: "
				  << dev.get_info<info::device::name>() << "\n";
		std::cout << " is_host(): "
				  << (dev.is_host() ? "Yes" : "No") << "\n";
		std::cout << " is_cpu(): "
				  << (dev.is_cpu() ? "Yes" : "No") << "\n";
		std::cout << " is_gpu(): "
			  	  << (dev.is_gpu() ? "Yes" : "No") << "\n";
		std::cout << " is_accelerator(): "
				  << (dev.is_accelerator() ? "Yes" : "No") << "\n";
				  
		do_query<info::device::vendor>(dev, "info::device::vendor");
		do_query<info::device::driver_version>(dev,
					"info::device::driver_version");
		do_query<info::device::max_work_item_dimensions>(dev,
					"info::device::max_work_item_dimensions");
		do_query<info::device::max_work_group_size>(dev,
					"info::device::max_work_group_size");
		do_query<info::device::mem_base_addr_align>(dev,
					"info::device::mem_base_addr_align");
		do_query<info::device::partition_max_sub_devices>(dev,
					"info::device::partition_max_sub_devices");
					
		std::cout << " Many more queries are available than shown here!\n";
	}
	std::cout << "\n";
}
\end{lstlisting}

\hspace*{\fill} \par %插入空行
\textbf{get\_info<>}

has\_extension()接口允许程序直接测试某个特性，而不是像前面的代码示例所显示的那样，从get\_info <info::platform::extensions>遍历扩展列表。SYCL 2020临时规范定义了新的机制来查询扩展和设备的详细信息，但本书中不涉及这些特性(它们刚刚定稿)。更多信息请参考在线oneAPI DPC++语言手册。
















